Sekvenciranje peptida, utvrđivanje lanca aminokiselina koje grade peptid, vrši se spektrometrijom. Ova metoda omogućava merenje odgovarajućih fizičkih svojstava (mase, naelektrisanja, ...) različitih fragmenata peptida čije vrednosti formiraju spektar. Fragmenti peptida predstavljaju uzastopne podsekvence aminokiselina, pri čemu su poznate vrednosti odabranih fizičkih osobina pojedinačnih aminokiselina (npr. njihove mase). Na osnovu dobijenog spektra, moguće je utvrditi koji redosled aminokiselina može dati takav spektar. U praksi, teorijski, idealni spektar koji ne sadrži greške se ne dešava, usled nesavršenosti metoda merenja vrednosti, pa je zato potrebno uzeti i takve slučajeve u obzir.
# Mase aminokiselina
amino_acid_masses = {
'': 0,
'G': 57,
'A': 71,
'S': 87,
'P': 97,
'V': 99,
'T': 101,
'C': 103,
'I': 113,
'L': 113,
'N': 114,
'D': 115,
'K': 128,
'Q': 128,
'E': 129,
'M': 131,
'H': 137,
'F': 147,
'R': 156,
'Y': 163,
'W': 186,
}
# Klasa PeptideSequencing će sadržati sve potrebne metode
# za sekvenciranje peptida
class PeptideSequencing:
def __init__(self, amino_acid_masses):
self.amino_acid_masses = amino_acid_masses
# Generisanje teorijskog linearnog spektra peptide
def linear_spectrum(self, peptide):
n = len(peptide)
prefix_mass = [0]
for i in range(n):
aa = peptide[i]
mass = self.amino_acid_masses[aa]
last_mass = prefix_mass[-1]
prefix_mass.append(mass + last_mass)
spectrum = [0]
for i in range(n):
for j in range(i + 1, n + 1):
fragment_mass = prefix_mass[j] - prefix_mass[i]
spectrum.append(fragment_mass)
spectrum.sort()
return spectrum
# Generisanje teorijskog cikličnog spektra peptide
def cyclic_spectrum(self, peptide):
n = len(peptide)
prefix_mass = [0]
for i in range(n):
aa = peptide[i]
mass = self.amino_acid_masses[aa]
last_mass = prefix_mass[-1]
prefix_mass.append(mass + last_mass)
spectrum = [0]
peptide_mass = prefix_mass[-1]
for i in range(n):
for j in range(i + 1, n + 1):
fragment_mass = prefix_mass[j] - prefix_mass[i]
spectrum.append(fragment_mass)
if i > 0 and j < n:
spectrum.append(peptide_mass - fragment_mass)
spectrum.sort()
return spectrum
# Prosirivanje peptida is skupa nadovezivanjem
# pojedinačno svih mogućih aminokiselina
def expand(self, peptides):
new_peptides = set([])
for peptide in peptides:
for aa in self.amino_acid_masses:
if aa != '':
new_peptides.add(f'{peptide}{aa}')
return new_peptides
# Metod utvrđuje da li manji spektar predstavlja podskup
# multiskupa koji je reprezentovan većim spektrom
# (da li se svi elementi iz manjeg spektra nalaze u većem spektru,
# uzimajući u obzir i duplikate)
def consistent(self, smaller_spectrum, bigger_spectrum):
n = len(bigger_spectrum)
m = len(smaller_spectrum)
j = 0
for i in range(n):
s1 = bigger_spectrum[i]
s2 = smaller_spectrum[j]
if s1 == s2:
j += 1
elif s1 > s2:
return False
if j == m:
return True
return False
# Metod izračunava masu peptida
def mass(self, peptide):
total_mass = 0
for aa in peptide:
total_mass += self.amino_acid_masses[aa]
return total_mass
# Metod pronalazi peptide čiji se teorijski ciklični spektar
# poklapa sa zadatim spektrom
def cyclopeptide_sequencing(self, spectrum):
peptides = set([''])
parent_mass = spectrum[-1]
output = []
while len(peptides) > 0:
peptides = self.expand(peptides)
peptides_to_remove = []
for peptide in peptides:
mass = self.mass(peptide,)
if mass == parent_mass:
cyclic_spectrum = self.cyclic_spectrum(peptide)
if cyclic_spectrum == spectrum:
output.append(peptide)
peptides_to_remove.append(peptide)
elif not self.consistent(self.linear_spectrum(peptide), spectrum):
peptides_to_remove.append(peptide)
for peptide in peptides_to_remove:
peptides.remove(peptide)
return output
# Metod prebrojava koliko je elemenata manjeg spektra
# pronađeno u većem spektru, uzimajući u obzir i duplikate
def score(self, smaller_spectrum, bigger_spectrum):
n = len(bigger_spectrum)
m = len(smaller_spectrum)
j = 0
i = 0
intersect = 0
while i < n:
s1 = bigger_spectrum[i]
s2 = smaller_spectrum[j]
if s1 == s2:
j += 1
i += 1
intersect += 1
elif s1 < s2:
i += 1
elif s2 < s1:
j += 1
if j == m:
j -= 1
break
return intersect
# Metod redukuje leaderboard skup tako da sadrži samo
# peptide sa najboljih N skorova u odnosu na zadati spektar
def trim(self, leaderboard, spectrum, N):
mapped_leaderboard = []
for peptide in leaderboard:
peptide_score = self.score(self.linear_spectrum(peptide), spectrum)
mapped_leaderboard.append((peptide, peptide_score))
n = len(mapped_leaderboard)
mapped_leaderboard.sort(key=lambda x: x[1], reverse=True)
i = N
for i in range(N, n):
if mapped_leaderboard[i][1] < mapped_leaderboard[i - 1][1]:
break
return set([x[0] for x in mapped_leaderboard[:i]])
# Metod pronalazi peptid čiji je ciklični spektar najsličniji zadatom spektru
# dozvoljavajući (u određenoj meri) greške u spektru,
# koje se ogledaju u umetnutim, nedostajućim ili izmenjenim vrednostima spektra
def leaderboard_cyclopeptide_sequencing(self, spectrum, N):
leaderboard = set([''])
parent_mass = spectrum[-1]
leader_peptide = ''
leader_score = 0
while len(leaderboard) > 0:
leaderboard = self.expand(leaderboard)
peptides_to_remove = []
for peptide in leaderboard:
mass = self.mass(peptide)
if mass == parent_mass:
cyclic_spectrum = self.cyclic_spectrum(peptide)
peptide_score = self.score(cyclic_spectrum, spectrum)
if peptide_score > leader_score:
leader_score = peptide_score
leader_peptide = peptide
elif mass > parent_mass:
peptides_to_remove.append(peptide)
for peptide in peptides_to_remove:
leaderboard.remove(peptide)
leaderboard = self.trim(leaderboard, spectrum, N)
return leader_peptide
ps = PeptideSequencing(amino_acid_masses)
# Testiranje pronalaženja linearnog teorijskog spektra
ps.linear_spectrum('NQEL')
# Testiranje pronalaženja cikličnog teorijskog spektra
ps.cyclic_spectrum('NQEL')
# Testiranje proširivanja skupa peptida dodavanjem aminokiselina
ps.expand({'A'})
# Testiranje provere konzistentnosti spektara
smaller = [1,1,3,5]
bigger = [1,2,3,4,5]
print(ps.consistent(smaller, bigger))
print(ps.consistent([1,3,5], bigger))
# Testiranje izračunavanja mase peptida
ps.mass("NQEL")
# Testiranje metoda koji pronalazi peptide čiji
# ciklični spektar odgovara zadatom spektru
# Napomena, aminokiseline Q i K kao i aminokiseline E i I imaju međusobno iste mase
peptide = "NQEL"
spectrum = ps.cyclic_spectrum(peptide)
ps.cyclopeptide_sequencing(spectrum)
ps.score([1,2,3],[1,2,2,2,8])
leaderboard = ['NQE', 'QE', 'KWD', 'EE', 'LL']
spectrum = [0, 113, 114, 128, 129, 227, 242, 242, 257, 355, 356, 370, 371, 484]
N = 3
ps.trim(leaderboard, spectrum, N)
# Modifikovani ciklični spektar NQELNQELNQEL peptida sa umetnutim pogrešnim, modifikovanim postojećim i izbačenim vrednostima
# Napomena, aminokiseline Q i K kao i aminokiseline E i I imaju međusobno iste mase
spectrum = [0, 113, 113, 113, 114, 114, 114, 128, 128, 128, 129, 129, 129, 227, 227, 227, 242, 242, 242, 242, 242, 242, 257, 257, 257, 355, 355, 355, 356, 356, 356, 370, 370, 370, 371, 371, 371, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 597, 597, 597, 598, 598, 598, 612, 612, 612, 613, 613, 613, 711, 711, 711, 726, 726, 726, 726, 726, 726, 741, 741, 741, 839, 839, 839, 840, 840, 840, 854, 854, 854, 855, 855, 855, 968, 968, 968, 968, 968, 968, 968, 968, 968, 968, 968, 968, 1081, 1081, 1081, 1082, 1082, 1082, 1096, 1096, 1096, 1097, 1097, 1097, 1195, 1195, 1195, 1210, 1210, 1210, 1210, 1210, 1210, 1225, 1225, 1225, 1323, 1323, 1323, 1324, 1324, 1324, 1338, 1338, 1338, 1339, 1339, 1339, 1452]
N = 5
ps.leaderboard_cyclopeptide_sequencing(spectrum, N)